{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Design - Quantum Variables and Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In Qmod there are three types of quantum objects which are represented by quantum variables:\n",
    "1. **Qubit** (quantum bit) \n",
    "\n",
    "    * Written as `QBit` in the Python SDK\n",
    "    * Written as `qbit` in the native syntax\n",
    "\n",
    "2. **Qubit Array** \n",
    "\n",
    "    A sequence of qubits, should be thought of as a quantum register with definite number of qubits.\n",
    "    \n",
    "    * Written as `QArray` in the Python SDK\n",
    "    * Written as `qbit[]` in the native syntax\n",
    "    \n",
    "3. **Quantum Number**  \n",
    "\n",
    "    A qubit array (quantum register) that represents numbers. The numbers can be positive and negative integers, and it can be fixed point numbers (e.g. $-5.25$).  The quantum number object has a definite number of qubits (as every qubit array), and it has 2 properties for its numeric representation: if the number is signed or not (only positive or it represents negative numbers as well) and how many fractional digits it has (where is the decimal point).\n",
    "\n",
    "    * Written as `QNum` in the Python SDK\n",
    "    * Written as `qnum` in the native syntax\n",
    "\n",
    "As explained in the [Design page](../design/design.ipynb), every quantum variable needs to be declared and initialized before it is used."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Concrete Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's get started and understand these quantum variables through a concrete example.\n",
    "\n",
    "Our task is to design a quantum algorithm that flips the most significant bit (MSB) of a quantum number and to verify this.\n",
    "\n",
    "<details>\n",
    "<summary> What is the MSB? </summary>\n",
    "\n",
    "In a binary representation, every number can be represented by a bit string (a sequence of 0's and 1's). For example, the number $6$ can be represented by the bit string $110$. How come? In order to understand it we move bit by bit and sum up the multiplication of the bit value times $2$ to the power of the bit position.\n",
    "\n",
    "We start with the right most bit called the least significant bit (LSB) since its position is the 0th position and sum $0\\times 2^0 =0$ plus $1\\times 2^1=2$ for the middle bit plus $1\\times 2^2=4$ for the left most bit - the most significant bit (MSB) since its contribution to the sum is the most significant.\n",
    "\n",
    "</details>\n",
    "\n",
    "How to approach that? We will have a function called `flip_msb` that will receive a Qubit Array, and we will flip its MSB. This function will be called from the `main` function with some Quantum Number, and then we will verify we receive the correct number using a Qubit indicator.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `flip_msb` function receives a `QArray` variable named `reg` (shortcut for register), and it flips its MSB using the `X` gate. This gate operates on the qubit at the last position of the Qubit Array `reg` where the counting starts from $0$. One can see the property `len` of the register `reg` is used as part of the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:27:56.604468Z",
     "iopub.status.busy": "2024-05-07T13:27:56.604013Z",
     "iopub.status.idle": "2024-05-07T13:27:59.894935Z",
     "shell.execute_reply": "2024-05-07T13:27:59.890726Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import QArray, X, qfunc\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def flip_msb(reg: QArray):\n",
    "    X(reg[reg.len - 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An important thing to note is that the variable `reg` is not initialized within the scope of the function `flip_msb`. This can be seen by the lack of the `output` declaration in the scope of the function, and it means that `reg` is initialized before it is called in the function. \n",
    "\n",
    "Now to the `main` function. \n",
    "\n",
    "We have only one variable as an argument for the function which is the indicator if we manage to flip the MSB or not. This is a Qubit variable named `indicator`. \n",
    "\n",
    "Within the function itself we declare and initialize a Quantum Number named `x` with $4$ qubits. A general initialziation with `allocate` initializes the Quantum Number to the state $|0\\rangle = |0000\\rangle$ (this is true for Qubits and Qubit Arrays as well). The function `flip_msb` acts on the Quantum Number `x` in order to flip its MSB and to create the state $|1000\\rangle = |8\\rangle$.\n",
    "\n",
    "<details>\n",
    "<summary> NOTE </summary>\n",
    "\n",
    "As we don't declare if the Quantum Number is signed or not, or how many fractional digits it has, it is just initialized with no fractional digits and is not signed (see [`allocate_num`](https://docs.classiq.io/latest/reference-manual/platform/qmod/language-reference/quantum-types/) for initializing a Quantum Number with these options). \n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:27:59.900256Z",
     "iopub.status.busy": "2024-05-07T13:27:59.898856Z",
     "iopub.status.idle": "2024-05-07T13:27:59.905959Z",
     "shell.execute_reply": "2024-05-07T13:27:59.905183Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import Output, QBit, QNum, allocate\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(indicator: Output[QBit]):\n",
    "\n",
    "    x = QNum(\"x\")\n",
    "    allocate(4, x)\n",
    "    flip_msb(x)\n",
    "\n",
    "    indicator |= x == 8"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Finally, we initialize the `indicator` qubit with the numeric assignment of the expression $x==8$. That is, if indeed we manage to flip the MSB of `x` and to transform it to the state `|8\\rangle` then the state od the qubit `indicator` will be equal $|1\\rangle$ otherwise it will equal $|0\\rangle (i.e. indicating if we manage to complete the `flip_msb` operation or not)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary> `flip_msb` receives a Quantum Number or a Quantum Array? </summary>\n",
    "One might notice that the function `flip_msb` is called with a Quantum Number `x` from the `main` function, whilst it is declared with a quantum array named `reg`. This demonstrates that quantum numbers can be cast to quantum arrays if they have the same number of qubits. In `main` we are intrested in the manupulation of an arithmetic quantum object - a quantum number, whilst in the function `flip_msb` we are just interested in treating the quantum number as a qubit array, without its numeric description, and to apply a specific gate on one of its qubits.\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's it! Now we want to check if indeed we managed to flip the MSB. The desired quantum program can be synthesized and viewed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:27:59.911654Z",
     "iopub.status.busy": "2024-05-07T13:27:59.910347Z",
     "iopub.status.idle": "2024-05-07T13:28:01.322215Z",
     "shell.execute_reply": "2024-05-07T13:28:01.321475Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/12d860cd-2de0-4ffa-9d81-8a1da9254c7f?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import create_model, show, synthesize\n",
    "\n",
    "quantum_program = synthesize(create_model(main))\n",
    "show(quantum_program)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div  style=\"text-align:center;\">\n",
    "    <img src=\"https://docs.classiq.io/resources/quantum_variables_and_functions.gif\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And then the quantum program can be executed to receive the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:01.375674Z",
     "iopub.status.busy": "2024-05-07T13:28:01.375367Z",
     "iopub.status.idle": "2024-05-07T13:28:02.763071Z",
     "shell.execute_reply": "2024-05-07T13:28:02.762315Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'indicator': 1.0}: 1000]\n"
     ]
    }
   ],
   "source": [
    "from classiq import execute\n",
    "\n",
    "job = execute(quantum_program)\n",
    "results = job.result()[0].value.parsed_counts\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div  style=\"text-align:center;\">\n",
    "    <img src=\"https://docs.classiq.io/resources/quantum_variables_and_functions_execution.gif\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And indeed we managed to flip the MSB as indicated by the `indicator` variable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary - Quantum Variables and Functions Guidelines"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following summarizes the main takeaways from the example in terms of quantum variables and functions:\n",
    "\n",
    "* There are 3 types of quantum objects in Qmod: a qubit, a qubit array, and a quantum number. Objects can be cast from one to another (as `x` being cast from quantum number in the `main` function to qubit array in the `flip_msb` function)\n",
    "* Quantum objects are represented by quantum variables. Each variable needs to be declared and initialized before it is used. The initialization can be done in several ways, here we see two options: with the `allocate` function (the initialization of `x`) or with a numeric assignment (the initializaiton of `indicator`).\n",
    "* Quantum variables that are arguments of a function can be declared with the `Output` modifier and initialized within the scope of a function (like `indicator` in the `main` function). Without the `Output` modifier they must be initialized outside the scope of the function (like `reg` in the `flip_msb`) function.\n",
    "* Quantum variables can be declared and initialized within a function (like `x` in the `main` function)\n",
    "* The quantum program is always generated from the `main` function, even when further quantum functions are used. The execution results of the quantum program are interperted only for the arguments of the `main` function (like for `indicator` in the above example)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:02.768179Z",
     "iopub.status.busy": "2024-05-07T13:28:02.767019Z",
     "iopub.status.idle": "2024-05-07T13:28:02.791636Z",
     "shell.execute_reply": "2024-05-07T13:28:02.790867Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import write_qmod\n",
    "\n",
    "write_qmod(create_model(main), \"quantum_variables_and_functions\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.11.7 ('classiq_devolpment')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  },
  "vscode": {
   "interpreter": {
    "hash": "e992e515f6583afc67b46eeabcda0f30363069fab8b382c7517b274ba7a59477"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
